﻿using PerformanceTests.Properties;
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;
using WAF.Content;
using System.IO;
using System.IO.Compression;


namespace PerformanceTests
{
    partial class Program
    {
        static byte[] testData;
        static string[] testText;

        static void Main(string[] args)
        {
            
            Stopwatch stopwatch = new Stopwatch();

            uint warmupTicks;
            byte[] compressedData;    
            int rounds = 10;
            long averageTicks;

            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2); // Uses the second Core or Processor for the Test
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;  // Prevents "Normal" processes from interrupting Threads
            Thread.CurrentThread.Priority = ThreadPriority.Highest;  // Prevents "Normal" Threads from interrupting this thread

            string[] separator = new string[] { "<" };
            string[] splitted = Resource.MseItWebSite.Split(separator, StringSplitOptions.RemoveEmptyEntries);
            testText = new string[99]; // splitted.Length];
            for (int i = 0; i < 99 /* splitted.Length */; ++i)
            {
                testText[i] = "<" + splitted[i];
            }
            testData = Encoding.UTF8.GetBytes(Resource.MseItWebSite);

            Console.WriteLine();
            Console.WriteLine();

            
            Deflater deflater = new Deflater();

            compressedData = CompressText(deflater, stopwatch, testText, true);
            CheckCompressedText(compressedData, testText, Encoding.UTF8);
            Console.WriteLine("Data Size: " + compressedData.Length + " uncompressed: " + testData.Length);

            warmupTicks = (uint)Environment.TickCount;
            while (((uint)Environment.TickCount) - warmupTicks < 1200)  // A Warmup of 1000-1500 mS stabilizes the CPU cache and pipeline.
            {
                CompressText(deflater, stopwatch, testText, true);
            }

            averageTicks = 0;
            for (int i = 0; i < rounds; ++i)
            {
                stopwatch.Reset();
                CompressText(deflater, stopwatch, testText, true);
                Console.WriteLine("Ticks: " + stopwatch.ElapsedTicks + " mS: " + ((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency) * 1000.0);
                averageTicks += stopwatch.ElapsedTicks;
            }
            averageTicks /= rounds;
            Console.WriteLine("Average Ticks: " + averageTicks + " mS: " + ((double)averageTicks / (double)Stopwatch.Frequency) * 1000.0);

            

            compressedData = Compress(deflater, stopwatch, testData, true);
            CheckCompressedData(compressedData, testData);
            Console.WriteLine("Data Size: " + compressedData.Length + " uncompressed: " + testData.Length);

            warmupTicks = (uint) Environment.TickCount;
            while (((uint)Environment.TickCount) - warmupTicks < 1200)  // A Warmup of 1000-1500 mS stabilizes the CPU cache and pipeline.
            {
                Compress(deflater, stopwatch, testData, true);
            }

            averageTicks = 0;
            for (int i = 0; i < rounds; ++i)
            {
                stopwatch.Reset();
                Compress(deflater, stopwatch, testData, true);
                Console.WriteLine("Ticks: " + stopwatch.ElapsedTicks + " mS: " + ((double) stopwatch.ElapsedTicks / (double) Stopwatch.Frequency) * 1000.0) ;
                averageTicks += stopwatch.ElapsedTicks;
            }
            averageTicks /= rounds;
            Console.WriteLine("Average Ticks: " + averageTicks + " mS: " + ((double)averageTicks / (double)Stopwatch.Frequency) * 1000.0);

            /*
            
            
            compressedData = CompressDeflateStream(stopwatch, testData);
            CheckCompressedData(compressedData, testData);
            Console.WriteLine("Data Size: " + compressedData.Length + " uncompressed: " + testData.Length);

            warmupTicks = (uint)Environment.TickCount;
            while (((uint)Environment.TickCount) - warmupTicks < 1200)  // A Warmup of 1000-1500 mS stabilizes the CPU cache and pipeline.
            {
                CompressDeflateStream(stopwatch, testData);
            }

            averageTicks = 0;
            for (int i = 0; i < rounds; ++i)
            {
                stopwatch.Reset();
                CompressDeflateStream(stopwatch, testData);
                Console.WriteLine("Stopwatch Ticks: " + stopwatch.ElapsedTicks + " mS: " + ((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency) * 1000.0);
                averageTicks += stopwatch.ElapsedTicks;
            }
            averageTicks /= rounds;
            Console.WriteLine("Average Ticks: " + averageTicks + " mS: " + ((double)averageTicks / (double)Stopwatch.Frequency) * 1000.0);

            */
            compressedData = SharpZipLibStream(stopwatch, testData);
            CheckCompressedData(compressedData, testData);
            Console.WriteLine("Data Size: " + compressedData.Length + " uncompressed: " + testData.Length);

            warmupTicks = (uint)Environment.TickCount;
            while (((uint)Environment.TickCount) - warmupTicks < 1200)  // A Warmup of 1000-1500 mS stabilizes the CPU cache and pipeline.
            {
                SharpZipLibStream(stopwatch, testData);
            }

            averageTicks = 0;
            for (int i = 0; i < rounds; ++i)
            {
                stopwatch.Reset();
                SharpZipLibStream(stopwatch, testData);
                Console.WriteLine("Stopwatch Ticks: " + stopwatch.ElapsedTicks + " mS: " + ((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency) * 1000.0);
                averageTicks += stopwatch.ElapsedTicks;
            }
            averageTicks /= rounds;
            Console.WriteLine("Average Ticks: " + averageTicks + " mS: " + ((double)averageTicks / (double)Stopwatch.Frequency) * 1000.0);

        }


        static public byte[] Compress(Deflater deflater, Stopwatch stopwatch, byte[] data, bool fast)
        {
            DeflaterConfig config = new DeflaterConfig();
            config.OutputBufferSize = 64000;
            config.Format = DeflaterConfig.StreamFormat.GZIP;
            config.StoreDataContext = new Result();
            config.StoreDataDelegate = ((Result) config.StoreDataContext).StoreData;
            config.FastCompression = fast;
            stopwatch.Start();
            deflater.Reset(config);
            deflater.Write(data, 0, data.Length);
            deflater.FinishWriting();
            stopwatch.Stop();
            return ((Result)config.StoreDataContext).ToArray();
        }


        static public byte[] CompressText(Deflater deflater, Stopwatch stopwatch, string[] text, bool fast)
        {
            DeflaterConfig config = new DeflaterConfig();
            config.OutputBufferSize = 64000;
            config.Format = DeflaterConfig.StreamFormat.DEFLATE;
            config.StoreDataContext = new Result();
            config.StoreDataDelegate = ((Result)config.StoreDataContext).StoreData;
            config.FastCompression = fast;
            stopwatch.Start();
            deflater.Reset(config);
            StringBuilder builder = new StringBuilder();
            foreach (string t in text)
            {
                builder.Append(t);
                // deflater.Write(t, 0, t.Length, Encoding.UTF8);
            }
            string res = builder.ToString();

            deflater.Write(res, 0, res.Length, Encoding.UTF8);
            deflater.FinishWriting();
            stopwatch.Stop();
            return ((Result)config.StoreDataContext).ToArray();
        }

        static public byte[] CompressDeflateStream(Stopwatch stopwatch, byte[] data)
        {
            MemoryStream stream = new MemoryStream();
            var compression = new DeflateStream(stream, CompressionMode.Compress);
            GC.Collect();
            stopwatch.Start();
            compression.Write(data, 0, data.Length);
            compression.Close();
            GC.Collect();
            stopwatch.Stop();
            return stream.ToArray();
        }

        static public byte[] SharpZipLibStream(Stopwatch stopwatch, byte[] data)
        {
            MemoryStream stream = new MemoryStream();
            var deflater = new ICSharpCode.SharpZipLib.Zip.Compression.Deflater(1,false);
            var compression = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream(stream, deflater, 32000);
            GC.Collect();
            stopwatch.Start();
            compression.Write(data, 0, data.Length);
            compression.Close();
            GC.Collect();
            stopwatch.Stop();
            return stream.ToArray();
        }

        public class Result
        {
            byte[] result = new byte[0];

            public void StoreData(byte[] buffer, int bufferLength, object conext)
            {

                result = AppendArrays(result, buffer, bufferLength);
            }

            public byte[] ToArray()
            {
                return result;
            }

            byte[] AppendArrays(byte[] a, byte[] b, int bLength)
            {
                byte[] c = new byte[a.Length + bLength]; // just one array allocation
                Buffer.BlockCopy(a, 0, c, 0, a.Length);
                Buffer.BlockCopy(b, 0, c, a.Length, bLength);
                return c;
            }

        }

    }
}
